/*******************************************************************************
 * Copyright 2011 Andrea Guitto
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.andreaguitto.mobile.android.framework.base.object.activity.multimedia.camera;

import java.io.IOException;

import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.OnZoomChangeListener;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.andreaguitto.mobile.android.framework.base.object.activity.AGActivity;

public abstract class AGCameraActivity extends AGActivity {    
    private AGCameraPreview cameraPreview;
   
	private SurfaceView cameraSurfaceView;  
    public SurfaceView getCameraSurfaceView() {	return cameraSurfaceView;	}
	public void setCameraSurfaceView(SurfaceView cameraSurfaceView) {	this.cameraSurfaceView = cameraSurfaceView;	}
	
	private boolean keepScreenOn = true;
	public boolean isKeepScreenOn(){	return keepScreenOn; }
	public void setKeepScreenOn(boolean state){	keepScreenOn = state; }
	
	private boolean inPreviewMode;
	public boolean isInPreviewMode() {	return inPreviewMode;	}
	public void setPreviewMode(boolean previewMode) {	this.inPreviewMode = previewMode; Log.d(getLogTag(), "PREVIEW MODE: "+previewMode);	}
	
	private int currentZoomStep=0;
	public int getCurrentZoomStep() { return currentZoomStep; }

	private int zoomUnit = 1;
	public void setZoomUnit(int unit){	zoomUnit = unit; }
	public int getZoomUnit(){	return zoomUnit; }
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        inPreviewMode = false;
    }
    
	public final void openCamera(){
    	
    	if(cameraPreview==null){
    		cameraPreview = new AGCameraPreview(this);
    	}
    	
	    cameraSurfaceView.getHolder().addCallback(cameraPreview);
	    cameraSurfaceView.getHolder().setKeepScreenOn(keepScreenOn);
	    cameraSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
    	
    	cameraPreview.openCamera();
    }
	
	public final void startPreview(){
		if(cameraPreview!=null && !isInPreviewMode()){
			cameraPreview.startPreview();
			setPreviewMode(true);
		}
	}
	
	public final void stopPreview(){
		if(cameraPreview!=null && isInPreviewMode()){
			cameraPreview.stopPreview();
			setPreviewMode(false);
		}
	}	
	
	public final void closeCamera(){
    	if(cameraPreview!=null){    
    		if(isInPreviewMode()) stopPreview();

    		cameraPreview.closeCamera();
    		if(cameraSurfaceView.getHolder()!=null)
    			cameraSurfaceView.getHolder().removeCallback(cameraPreview);
    		cameraPreview = null;
    	}
    }	

    public final void autofocus(){  
    	cameraPreview.autofocus();
    }	
	
    public final void takePicture(){
    	if(isInPreviewMode())
    		stopPreview();
    	
    	cameraPreview.takePicture();
    }
    
    public void startSmoothZoom(int value){
    	if(isInPreviewMode()){
    		cameraPreview.startSmoothZoom(value);
    	}
    }
    
    public void stopSmoothZoom(){
    	if(isInPreviewMode()){
    		cameraPreview.stopSmoothZoom();
    	}
    }
    
    public void setZoom(int step){
    	Camera.Parameters params = getCameraParameters();    	
    	if(isInPreviewMode() && params.isZoomSupported()){
	    	if(step <= params.getMaxZoom() && step >= 0){
	    		params.setZoom(step);
	    		currentZoomStep = step;
	    	}else if(step > params.getMaxZoom()){
	    		params.setZoom(params.getMaxZoom());
	    		currentZoomStep = params.getMaxZoom();
	    	}else if(step < 0){
	    		params.setZoom(0);
	    		currentZoomStep = 0;
	    	}
			setCameraParameters(params);
    	}
    }
    
    public void zoomIn(){
    	Camera.Parameters params = getCameraParameters();
    	if(isInPreviewMode() && params.isZoomSupported()){    	
	    	currentZoomStep=currentZoomStep>=params.getMaxZoom()?params.getMaxZoom():currentZoomStep+getZoomUnit();
	    	params.setZoom(currentZoomStep);
			setCameraParameters(params);
    	}
    }
    
    public void zoomOut(){
    	Camera.Parameters params = getCameraParameters();
    	if(isInPreviewMode() && params.isZoomSupported()){     	
	    	currentZoomStep=currentZoomStep<=0?0:currentZoomStep-getZoomUnit();
	    	params.setZoom(currentZoomStep);
			setCameraParameters(params);
    	}		
    }    
    
    public void onPictureTaken(byte[] image){
    	Log.d(getLogTag(), "onPictureTaken");
    }

    public void onAutoFocus(boolean success){
    	Log.d(getLogTag(), "onAutoFocus");
    }  
    
	public void onZoomChange(int zoomValue, boolean stopped) {	Log.d(getLogTag(), "onZoomChange "+zoomValue+" stopped: "+stopped); }    
    
    @Override
    public void onStop(){
    	super.onStop();
    	if(cameraPreview!=null){
    		
    		if(isInPreviewMode()) stopPreview();
    		
    		cameraPreview.closeCamera();
    		
    		if(cameraSurfaceView.getHolder()!=null)
    			cameraSurfaceView.getHolder().removeCallback(cameraPreview);
    		cameraPreview = null;
    	}
    }
    
    /**
     * Call only after openCamera(). 
     * Parameters changes apply only after the call to setCameraParameters()
     * 
     * @return
     */
    public Parameters getCameraParameters() {	return cameraPreview.getCameraParameters();	}
	public void setCameraParameters(Parameters params) {	cameraPreview.setCameraParameters(params);	}

    public void setDisplayOrientation(int degrees) {	cameraPreview.setDisplayOrientation(degrees);	}
	
}

// ----------------------------------------------------------------------

class AGCameraPreview implements SurfaceHolder.Callback {
    Camera camera;
    AGCameraActivity activity;
    
    AGCameraPreview(Context context) {
        super();
        activity = (AGCameraActivity) context;
    }

    PictureCallback jpegCallback = new PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {
                activity.onPictureTaken(data);
        }
    };
    
    
    AutoFocusCallback autofocusCallback = new AutoFocusCallback() {
		@Override
		public void onAutoFocus(boolean success, Camera camera) {
			activity.onAutoFocus(success);
		}
	};  
	
	OnZoomChangeListener zoomChangeListener = new OnZoomChangeListener() {
		
		@Override
		public void onZoomChange(int zoomValue, boolean stopped, Camera camera) {
			Log.d("CAMERA", "ZOOM CHANGED IN VALUE "+zoomValue);
			activity.onZoomChange(zoomValue, stopped);
		}
	};	
    
    public final void openCamera(){
    	if(camera==null){
    		camera=Camera.open();
    	}
    }
    
    public final void setDisplayOrientation(int degrees){
    	if(camera!=null)
    		camera.setDisplayOrientation(degrees);
    }
    
    public final void startSmoothZoom(int value){
    	if(camera!=null){
    		Log.d("CAMERA", "START ZOOM IN VALUE "+value);
    		camera.startSmoothZoom(value);
    	}
    }
    
    public final void stopSmoothZoom(){
    	if(camera!=null){
    		Log.d("CAMERA", "STOP ZOOM");
    		camera.stopSmoothZoom();
    	}
    }    
    
    /**
     * Call only after openCamera()
     * 
     * @return
     */
    public Camera.Parameters getCameraParameters(){	return camera!=null?camera.getParameters():null;	}
    public void setCameraParameters(Camera.Parameters params){	camera.setParameters(params);	}    
    
    public final void autofocus(){
    	camera.autoFocus(autofocusCallback);  	
    }
    
    public final void takePicture(){
    	camera.takePicture(null, null, jpegCallback);
    }

    public final void startPreview(){
    	if(camera!=null && !activity.isInPreviewMode()){
    		camera.startPreview();
    	}
    }    
    
    public final void stopPreview(){
    	if(camera!=null && activity.isInPreviewMode()){
    		camera.stopPreview();
    	}
    }
    
    public final void closeCamera(){
    	if(camera!=null){    		
	    	camera.release();
	    	camera = null;
    	}
    }

    
	@Override
    public void surfaceCreated(SurfaceHolder holder) {
		if(holder!=null && camera!=null){
	        try {
	    		camera.setZoomChangeListener(zoomChangeListener);
	        	camera.startSmoothZoom(4);	        	
	        	camera.setPreviewDisplay(holder);
	        } catch (IOException exception) {
	        	closeCamera();
	        }
		}
    }
    
	@Override
    public void surfaceDestroyed(SurfaceHolder holder) {
		closeCamera();
    }

	@Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		if(camera!=null){
	        openCamera();
		}
    }


}
